package com.mnemonic.utils;

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.charset.UnsupportedCharsetException;
import java.util.ArrayList;
import java.util.List;

/**
 * 暂时 简单的使用 bio 中的文件读取方式， 主要添加nio 的文件操作方式
 *
 * @author: lifeng
 * @date: 2020/3/29 14:13
 */
public class IOUtils {

    /**
     * 默认编码
     **/
    public static Charset DEFAULT_ENCODING = StandardCharsets.UTF_8;

    /**
     * @param closeable
     *            the object to close, may be null or already closed
     * @throws IOException
     *             异常
     * @since 2.0
     */
    public static void close(Closeable closeable) throws IOException {
        if (closeable != null) {
            closeable.close();
        }
    }

    /**
     * Unconditionally close a <code>Closeable</code>.
     * <p>
     * Equivalent to {@link Closeable#close()}, except any exceptions will be ignored. This is typically used in finally
     * blocks.
     * <p>
     * Example code:
     *
     * <pre>
     * Closeable closeable = null;
     * try {
     *     closeable = new FileReader("foo.txt");
     *     // process closeable
     *     closeable.close();
     * } catch (Exception e) {
     *     // error handling
     * } finally {
     *     IOUtils.closeQuietly(closeable);
     * }
     * </pre>
     *
     * @param closeable
     *            the object to close, may be null or already closed
     * @since 2.0
     */
    public static void closeQuietly(Closeable closeable) {
        try {
            if (closeable != null) {
                closeable.close();
            }
        } catch (IOException ioe) {
            // ignore
        }
    }

    /**
     * Get the contents of an <code>InputStream</code> as a list of Strings, one entry per line, using the specified
     * character encoding.
     * <p>
     * Character encoding names can be found at <a href="http://www.iana.org/assignments/character-sets">IANA</a>.
     * <p>
     * This method buffers the input internally, so there is no need to use a <code>BufferedInputStream</code>.
     *
     * @param input
     *            the <code>InputStream</code> to read from, not null
     * @return the list of Strings, never null
     * @throws NullPointerException
     *             if the input is null
     * @throws IOException
     *             if an I/O error occurs
     * @throws UnsupportedCharsetException
     *             thrown instead of {@link UnsupportedEncodingException} in version 2.2 if the encoding is not
     *             supported.
     * @since 1.1
     */
    public static List<String> readLines(InputStream input) throws IOException {
        return readLines(input, StandardCharsets.UTF_8);
    }

    /**
     * Get the contents of an <code>InputStream</code> as a list of Strings, one entry per line, using the specified
     * character encoding.
     * <p>
     * Character encoding names can be found at <a href="http://www.iana.org/assignments/character-sets">IANA</a>.
     * <p>
     * This method buffers the input internally, so there is no need to use a <code>BufferedInputStream</code>.
     *
     * @param input
     *            the <code>InputStream</code> to read from, not null
     * @param encoding
     *            the encoding to use, null means platform default
     * @return the list of Strings, never null
     * @throws NullPointerException
     *             if the input is null
     * @throws IOException
     *             if an I/O error occurs
     * @throws UnsupportedCharsetException
     *             thrown instead of {@link UnsupportedEncodingException} in version 2.2 if the encoding is not
     *             supported.
     * @since 1.1
     */
    public static List<String> readLines(InputStream input, String encoding) throws IOException {
        Charset charset = encoding == null ? DEFAULT_ENCODING : Charset.forName(encoding);
        return readLines(input, charset);
    }

    /**
     * Get the contents of an <code>InputStream</code> as a list of Strings, one entry per line, using the specified
     * character encoding.
     * <p>
     * This method buffers the input internally, so there is no need to use a <code>BufferedInputStream</code>.
     *
     * @param input
     *            the <code>InputStream</code> to read from, not null
     * @param encoding
     *            the encoding to use, null means platform default
     * @return the list of Strings, never null
     * @throws NullPointerException
     *             if the input is null
     * @throws IOException
     *             if an I/O error occurs
     * @since 2.3
     */
    public static List<String> readLines(InputStream input, Charset encoding) throws IOException {
        Charset charset = encoding == null ? DEFAULT_ENCODING : encoding;
        InputStreamReader reader = new InputStreamReader(input, charset);
        return readLines(reader);
    }

    /**
     * Get the contents of a <code>Reader</code> as a list of Strings, one entry per line.
     * <p>
     * This method buffers the input internally, so there is no need to use a <code>BufferedReader</code>.
     *
     * @param input
     *            the <code>Reader</code> to read from, not null
     * @return the list of Strings, never null
     * @throws NullPointerException
     *             if the input is null
     * @throws IOException
     *             if an I/O error occurs
     * @since 1.1
     */
    public static List<String> readLines(Reader input) throws IOException {
        BufferedReader reader = toBufferedReader(input);
        List<String> list = new ArrayList<>();
        String line = reader.readLine();
        while (line != null) {
            list.add(line);
            line = reader.readLine();
        }
        return list;
    }

    /**
     * Returns the given reader if it is a {@link BufferedReader}, otherwise creates a toBufferedReader for the given
     * reader.
     *
     * @param reader
     *            the reader to wrap or return
     * @return the given reader or a new {@link BufferedReader} for the given reader
     * @since 2.2
     */
    public static BufferedReader toBufferedReader(Reader reader) {
        return reader instanceof BufferedReader ? (BufferedReader)reader : new BufferedReader(reader);
    }

    /**
     * 读取目标类相同目录下的文件
     * 
     * @param target
     * @param fileName
     * @return
     */
    public static String readAsText(Class<?> target, String fileName) {
        InputStream is = null;
        try {
            is = target.getResourceAsStream(fileName);
            List<String> lines = IOUtils.readLines(is);
            return StringUtils.join(lines, "\n");
        } catch (Exception e) {
            return StringUtils.EMPTY;
        } finally {
            IOUtils.closeQuietly(is);
        }
    }

    /**
     * 关闭文件通道
     *
     * @param channel
     *            文件通道
     */
    public static void closeQuietly(FileChannel channel) {
        if (channel != null) {
            try {
                channel.close();
            } catch (IOException ignored) {
            }
        }
    }

    /**
     * 将 字节写入文件
     *
     * @param file
     *            文件
     * @param datas
     *            数据
     */
    public static void write(File file, byte[] datas) {
        FileChannel channel = null;
        FileOutputStream out = null;
        try {
            out = new FileOutputStream(file);
            channel = out.getChannel();
            channel.write(ByteBuffer.wrap(datas));
        } catch (Exception ignored) {
        } finally {
            IOUtils.closeQuietly(out);
            IOUtils.closeQuietly(channel);
        }
    }

    /**
     * 打开这个文件
     *
     * @param file
     *            文件
     * @return 数据
     */
    public static byte[] read(File file) {
        FileInputStream fis = null;
        ByteArrayOutputStream out = null;
        try {
            fis = new FileInputStream(file);
            out = new ByteArrayOutputStream();
            byte[] buffer = new byte[512];
            int n;
            while ((n = fis.read(buffer)) != -1) {
                out.write(buffer, 0, n);
            }
            return out.toByteArray();
        } catch (Exception e) {
            return null;
        } finally {
            IOUtils.closeQuietly(fis);
            IOUtils.closeQuietly(out);
        }
    }
}
